<?php
class Model{
    protected $table = null;
    protected $full_table = null;
    protected $db = null;
    protected $prefix = null;
    protected $alias = null;
    protected $text = 0;
    public$join = [];
    protected $queries  = [];
    protected $sql= ['select'=>'','field'=>'','where'=>'','order'=>'', 'limit'=> '','group'=> '','having'=>''];
    public function __construct($table='',$mod = 0){
        if(empty($table)){
            if(empty($this->table)){
                $this->table =ltrim(strtolower(substr(get_class($this), 0,-5)));
            }
        }else{
            $this->table = $table;
        }
        if(!empty($this->table)){
            switch($mod){
            case 0:
                $this->db = Database_Connector_Read::getInstance();
                break;
            case 1:
                $this->db = Database_Connector_Write::getInstance();
                break;
            }
            if($this->prefix == null){
                $config = $this->db->getConfig();
                $prefix = isset($config->master->prefix) ? $config->master->prefix : '';
                $this->prefix = $prefix;
                $this->full_table = $prefix.$this->table;
            }
        }else{
            return false;
        }
        // $this->db->exec('SET SQL_MODE=ANSI_QUOTES');
    }

    public function table($table) {
        $this->table = $table;
        $this->full_table = $this->prefix.$this->table;
        return $this;
    }
    public function prefix($name){
        return $this->prefix.$this->table;
    }
    public function alias($alias){
        $this->alias = $alias;
        return $this;
    }
    private function join($join, $condition, $params = array()){
        $str = $join . ' ON ' . $condition;
        $this->join['str'][] = $str;
        $this->join['par'][] = $params;
        return $this;
    }
 
    public function left( $join, $condition, $params = array() ){
        $join_table = $this->prefix.$join[0];

        return $this->join( ' LEFT JOIN ' . $join_table.' AS '.$join[1], $condition, $params );
    }
 
    public function right( $join, $condition, $params = array() )
    {
        return $this->join( ' RIGHT JOIN ' . $join, $condition, $params );
    }
 
    public function inner( $join, $condition , $params = array() )
    {
        return $this->join( ' INNER JOIN ' . $join , $condition, $params  );
    }
 
    protected function setNull() {
        $this->sql   = array(
            'select'  => '',
            'field' => '',
            'where'   => '',
            'order'   => '',
            'limit'   => '',
            'group'   => '',
            'having'  => '',
        );
        $this->tableName = NULL;
        $this->join  = array();
        $this->text  = 0;
        $this->host  = '';
    }
    public function __get( $name )
    {
        if ( $name == 'text' )
            $this->text  = 1;
        return $this;
    }
    public function __call( $methodName, $args ){
        $methodName = strtolower( $methodName );
        if (array_key_exists($methodName , $this->sql)){
            if ( empty( $args[0] ) || ( is_string( $args[0] ) AND trim( $args[0] === ''))){
                $this->sql[$methodName]  = '';
            }else{
                $this->sql[$methodName]  = $args;
            }
            if ( $methodName == 'limit' ){
                $this->sql[$methodName]  = $args;
            }
        }else if ( $methodName == 'from' ){
            $this->tableName = $args[0];
        }else if ( $methodName == 'host'){
            $this->host  = $args;
        }else{
            echo 'class ' . get_class( $this ) . " function $methodName not exists";
        }
        return $this;
    }
    public function field($field = []){
        if(empty($field)){
            $this->sql['field'] = '*';
        }else{
            // var_dump($field);
            $this->sql['field'] = implode(",", $field);
        }
        // var_dump($this->sql['field']);
        return $this;
    }
    public function select(){
        $field = '';
        if(empty($this->sql['field'])){
            $field = '*';
        }else{
            $field = $this->sql['field'];
        }

        $where  = '';
        $data   = array();
        $join   = '';
        if ( isset( $this->join['str'] ) ){
            foreach ( $this->join['str'] as $t_join ){
                $join   .= $t_join;
            }
            foreach ( $this->join['par'] as $params ){
                foreach ( $params as $param){
                    $data[] = $param;
                }
            }
        } 
        if($this->sql['where'] !== ''){
            $where = $this->collate_where( $this->sql['where'] );
            foreach($where['data'] as $v){
                $data[] = $v;
            }
            $where  = ' WHERE ' . $where['where'];
        }
 
        $group  = '';
        if ( $this->sql['group'] != '' )
        {
            $group  = " GROUP BY " . implode( ',', array_fill( 0, count( $this->sql['group'] ) , '?' ));
            foreach ( $this->sql['group'] as $val )
                $data[] = $val;
        }
        $having = '';
        if ( $this->sql['having'] != ''){
            $having = $this->comWhere( $this->sql['having'] );
            foreach ( $having['data'] as $val ){
                $data[] = $val;
            }
            $having = ' HAVING ' . $having['where'];
        }
        $order  = '';
        if ( $this->sql['order'] != ''){
            $order  = ' ORDER BY ' . $this->sql['order'][0];
        }
        $limit  = '';
        if ( $this->sql['limit'] != '' ){
            $limit  = $this->collate_limit( $this->sql['limit'] );
        }
        // var_dump($limit);
        if($this->alias == null){
            $sql = "SELECT $field FROM " . $this->full_table . " $join $where $group $having $order $limit";
        }else{
            $sql = "SELECT $field FROM " . $this->full_table . ' AS '.$this->alias . " $join $where $group $having $order $limit";
        }
        // var_dump($sql);
        $result = null;
        if(strpos($sql, '?') === FALSE ){
            $result = $this->db->query( $sql )->fetchAll(PDO::FETCH_ASSOC);
            $this->setNull();
        }else{
            // var_dump($data);
            $prepare= $this->db->prepare( $sql );
            $result = $prepare->execute( $data );
            $result = $prepare->fetchAll(PDO::FETCH_ASSOC);
        }
        return $result;
        // var_dump($result);
    }
    public function one(){
        $result = $this->select();
        if(empty($result)){
            return false;
        }else{
            return $result[0];
            // var_dump($result);
        }
    }
    function insert( $array = null ) {
        $sql = "INSERT INTO {$this->full_table}("
             . implode(',', array_keys($array)).") VALUES ("
             . implode(',', array_fill(0, count($array), '?')) . ")";
        $result = $this->pdo_query( $sql, array_values( $array ) );
        return $result;
    }
 
    function update( $array = null ){
        $data   = array();
        if( is_array( $array )){
            $s = '';
            foreach($array as $k => $v){
                $s  .= " $k = ? ,";
                $data[]  = $v;
            }
            $s = rtrim( $s, ',' );
        }else{
            return;
        }
        $limit  = '';
        if($this->sql['where'] != ''){
            $where  = $this->collate_where($this->sql['where']);
            $sql= "UPDATE " . $this->full_table . " SET $s WHERE " . $where['where'];
            if ( ! empty($where['data'])){
                foreach ($where['data'] as $v)
                    $data[] = $v;
            }
            $order  = '';
            if($this->sql['order'] != ''){
                $order  = ' ORDER BY ' . $this->sql['order'][0];
            }
            if($this->sql['limit'] != '' ){
                $limit = $this->collate_limit( $this->sql['limit'] );
            }
            $sql .= $order . $limit;
        }else{
            $sql = "UPDATE " . $this->full_table . " SET $s LIMIT 1";
        }
        // var_dump($sql);
        try{
            $result = '';
            $sql= str_replace( '<<_', $this->full_table, $sql );
            // var_dump($sql);
            // exit;
 
            if($this->text == 1){
                $text[] = $sql;
                $text[] = $this->sql( $sql, $data );
                $this->setNull();
                // return $text;
            }
 
            $prepare = $this->db->prepare($sql);

            $prepare->execute($data); 
            $result = $prepare->rowCount();
            return $result;
            // var_dump($prepare);
        }catch( PDOException $e ){
            $this->error = $e->getMessage();
            return false;
        }
        // $this->pdo_query($sql,$data);
    }
 
    function delete(){
        $where  = '';
        $data   = array();
        if($this->sql['where'] != ''){
            $where  = $this->collate_where( $this->sql['where'] );
            $data   = $where['data'];
            $where  = ' WHERE ' . $where['where'];
        }
        $select = $this->sql['select'] != '' ? $this->sql['select'][0] : '';
 
        $order  = '';
        if ($this->sql['order'] != ''){
            $order  = ' ORDER BY ' . $this->sql['order'][0];
        }
 
        $limit  = '';
        if( $this->sql['limit'] != '' ){
            $limit  = $this->collate_limit( $this->sql['limit'] );
        }
        if( $where == '' ){
            $limit  = ' LIMIT 1 ';
        }
        $sql= 'DELETE ' . $select. ' FROM ' . $this->full_table . " $where $order $limit ";
        // var_dump($sql);
        try{
            $prepare = $this->db->prepare($sql);
            $prepare->execute($data);
            $result = $this->db->lastInsertid();
            return $result;
        }catch( PDOException $e ){
            $this->error = $e->getMessage();
            return false;
        }
    }
 
    private function collate_limit($args){
        if ( isset( $args[1] ))
            $limit = ' LIMIT ' . (int)$args[0] . ' , ' . (int)$args[1];
        else
            $limit = ' LIMIT ' . (int)$args[0];
        return $limit;
    }
    // public function where($arg=[]){
    // $this->sql['where'][0] = $arg[0];
    // $data = explode(',',$arg[1]);
    // $this->sql['where'][1] = $data;
    // return $this;
    // }

    //where(['$file_name'=>$value])
    public function collate_where($args)
    {
        $where  = '';
        $data   = [];
        if(empty($args)){
            return array( 'where' => '' , 'data' => $data );
        }else{
            foreach($args as $option){
                if (empty($option)){
                    $where  .= '';
                    continue;
                }else if(is_array($option)){
                    foreach ( $option as $k => $v ) {
                        if(is_array($v)){
                            if(strpos( $k, '?' )){
                                $where  .= $k;
                            }else{
                                $where  .= $k . " IN (" . implode( ',', array_fill( 0, count($v), '?' )). ")";
                            }
                            foreach ( $v as $val ){
                                $data[] = $val;
                            }
                        } else if(strpos( $k, ' ')){
                            $where  .= $k . '?';
                            $data[] = $v;
                        } else{
                            $where  .= "$k = ?";
                            $data[] = $v;
                        }
                        $where  .= ' AND ';
                    }
                    $where = rtrim( $where, 'AND ' );
                    $where .= ' OR ';
                    continue;
                }
            }
        }
        $where  = rtrim($where, 'OR ');
        // var_dump($where);
        $result = ['where' => $where, 'data' => $data];
        // var_dump($result);
        return $result;
    }
 
    public function sql($sql, $params){
        if ( false === strpos( $sql, '?' ))
            return $sql;
 
        $sql= str_replace( '?', "%s", $sql );
        array_unshift( $params, $sql );
        return call_user_func_array( 'sprintf', $params );
    }
    public function pdo_query($sql, $data = array()){
        try{
            $result = '';
            $sql= str_replace( '<<_', $this->full_table, $sql );
            // var_dump($sql);
            // exit;
 
            if($this->text == 1){
                $text[] = $sql;
                $text[] = $this->sql( $sql, $data );
                $this->setNull();
                // return $text;
            }
 
            if(strpos($sql, '?') === FALSE ){
                $result = $this->db->query( $sql )->fetchAll(PDO::FETCH_ASSOC);
                $this->setNull();
            }else{
                switch(strtolower($sql{0})){
                case 'i' :
                    $prepare= $this->db->prepare( $sql );
                    $prepare->execute( $data );
                    $result = $this->db->lastInsertid();
                    break;
                case 'd' :
                case 'u' :
                    $prepare = $this->db->prepare($sql);
                    $prepare->execute($data); 
                    $result = $prepare->rowCount();
                    break;
                default :
                    $result = $prepare->fetchAll(PDO::FETCH_ASSOC);
                }
            }
            return $result;

        }catch( PDOException $e ){
            $this->error = $e->getMessage();
            return false;
        }
 
    }
 
    public function begintransaction(){
        $this->db->setAttribute(PDO::ATTR_AUTOCOMMIT, 0);
        $this->db->beginTransaction();
    }
 
    public function commit(){
        $this->db->commit();
        $this->db->setAttribute(PDO::ATTR_AUTOCOMMIT, 1);
    }
 
    public function rollback(){
        $this->db->rollBack();
        $this->db->setAttribute(PDO::ATTR_AUTOCOMMIT, 1);
    }
    public function version(){
        return $this->db->getAttribute(PDO::ATTR_SERVER_VERSION);
    }
    public function count($id = 'id'){
        if(empty($this->sql['where'])){
            $sql = 'select `table_rows` from information_schema.TABLES where table_name = \''.$this->full_table.'\'';
            $result = $this->db->query($sql)->fetchAll(PDO::FETCH_ASSOC);
            return $result[0]['table_rows'];
        }else{
            // Mark....
            $where_array = $this->collate_where($this->sql['where']);
            $where  = ' WHERE ' . $where_array['where'];
            foreach($where_array['data'] as $v){
                $data[] = $v;
            }
            $statement = 'select count(1) from '.$this->full_table.' '.$where;
            $prepare= $this->db->prepare($statement);
            $result = $prepare->execute($data);
            $result = $prepare->fetchAll(PDO::FETCH_NUM);
            return $result[0];
        }
    }
}
